/*
 * Copyright (C) 2018-2020 harryguo
 * 本类的作用：客户源码在编译过程中，用本类来生成代码，该代码记录下所有@Subscribe的静态函数，在post/process时自动register。
 * 所以，这里再重复声明一下：静态函数的@Subscribe是由EventBus系统自动注册的，这些想监听事件的静态函数不会错过任何一个Event。
 */
package org.greenrobot.eventbus.annotationprocessor;

import org.greenrobot.eventbus.Subscribe;

import java.util.Set;
import java.util.TreeMap;

import javax.annotation.processing.Filer;
import javax.annotation.processing.Messager;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.element.Element;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.tools.Diagnostic;

public class AutoRegisterProcessor {
    private Filer mFiler; //文件相关的辅助类
    private Messager mMessager; //日志相关的辅助类

    private TreeMap<String, SubscribeClass> mSubscribeClazzMap = new TreeMap<>();

    public synchronized void init(ProcessingEnvironment processingEnv) {
        mFiler = processingEnv.getFiler();
        mMessager = processingEnv.getMessager();
    }

    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment env, boolean indexFileCreated) {
        mSubscribeClazzMap.clear();

        try {
            processSubScribeAnnotation(env);
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
            printError(e.getMessage());
        }
        AutoRegisterCreator creator = new AutoRegisterCreator(mSubscribeClazzMap.values());
        try {
            creator.createFile(mFiler, indexFileCreated);
        } catch (Exception e) {
            e.printStackTrace();
            printError(e.getMessage());
        }
        return true;
    }

    private void processSubScribeAnnotation(RoundEnvironment roundEnv) throws IllegalArgumentException {
        for (Element element : roundEnv.getElementsAnnotatedWith(Subscribe.class)) {
            // 只处理静态订阅者（静态订阅者，是由我们EventBus默认注册的）
            if (element.getModifiers().contains(Modifier.STATIC)) {
                SubscribeClass annotatedClass = getSubscribeClass(element);
                SubscribeMethodElement subscribedMethod = new SubscribeMethodElement(element);
                annotatedClass.addMethod(subscribedMethod);
            }
        }
    }

    /**
     * 获取/创建含有@Subscribe的函数的类
     * @param element 使用了注解@Subscribe的函数
     * */
    private SubscribeClass getSubscribeClass(Element element) {
        TypeElement typeElement = (TypeElement) element.getEnclosingElement();
        // 包含@Subscribe 静态订阅者的类的全路径名（也就是包含了包名）
        String fullClassName = typeElement.getQualifiedName().toString();
        SubscribeClass annotatedClass = mSubscribeClazzMap.get(fullClassName);
        if (annotatedClass == null) {
            annotatedClass = new SubscribeClass(typeElement);
            mSubscribeClazzMap.put(fullClassName, annotatedClass);
        }
        return annotatedClass;
    }

    private void printError(String msg, Object... args) {
        mMessager.printMessage(Diagnostic.Kind.ERROR, String.format(msg, args));
    }
}
